// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include <nu_api.h>
#include <mutex.h>
#include <zenr_api.h>

#define MINISD_IOCTL_BASE                   0x1000
#define MINISD_IOCTL_OPEN                   (MINISD_IOCTL_BASE + 1)
#define MINISD_IOCTL_CLOSE                  (MINISD_IOCTL_BASE + 2)
#define MINISD_IOCTL_CAPACITY               (MINISD_IOCTL_BASE + 3)


#define MINISD_INSERTED     0x01
#define MINISD_REMOVED      0x02
#define MINISD_FINISHED     0x03
#define MINISD_REFRESHED    0x04

class CMiniSd : public CDeviceDriver {
public:
    CARAPI Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf &buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf &inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);


    static ECode ListeningRoutine(void *arg);

    virtual void Dispose() {};

    CMiniSd() : m_pLock(NULL), m_sdRefreshEvent(FALSE, UNSIGNALED),
        m_pListenThread(NULL), m_nCountSectors(-1), m_nbWorking(1),
        m_nCardStatus(0)
    {
        Control(MINISD_IOCTL_OPEN, NULL_MEMORYBUF, NULL, NULL);

        m_nCountSectors = nu_zenr_gbi_get_tfcard_blocks();

        m_pLock = new CMutex;
        if (m_pLock) {
            m_pLock->AddRef();
            kprintf("MiniSD: Init OK, Capacity is %d sectors\n",
                                                        m_nCountSectors);
        }
        else {
            kprintf("MiniSD: Create mutex failed, out of memory!!!\n");
        }

        DzCreateKernelThread(
            &ListeningRoutine, (PVoid)this, CreateThreadFlag_System,
            ThreadPriorityRank_Normal, &m_pListenThread);
    }

    virtual ~CMiniSd()
    {
        m_nbWorking = 0;
        m_sdRefreshEvent.Notify(MINISD_FINISHED);
        Control(MINISD_IOCTL_CLOSE, NULL_MEMORYBUF, NULL, NULL);
        m_pListenThread->Join(10000, NULL);
        m_pListenThread->Release();
        m_pLock->Release();
    }

    void Lock()
    {
        m_pLock->Lock(NULL);
    }

    void Unlock()
    {
        m_pLock->Unlock();
    }

private:
    IMutex* m_pLock;
    DzEvent m_sdRefreshEvent;
    IThread* m_pListenThread;
    Int32 m_nCountSectors;
    Int16 m_nbWorking;
    Int16 m_nCardStatus;
};

ECode CMiniSd::ListeningRoutine(void *arg)
{
    CMiniSd* pThis = (CMiniSd*)arg;
    WaitResult wr;
    EventState stat;

    nu_RegisterSDCardEvent((Int32)&pThis->m_sdRefreshEvent);

    while (pThis->m_nbWorking) {
        pThis->m_sdRefreshEvent.Wait(&wr, &stat);
        if (wr != WaitResult_OK) break;

        if (stat != pThis->m_nCardStatus) {
            if (stat == MINISD_INSERTED) {
                MemoryBuf_<16> ebBuf;

                *(int *)ebBuf.GetPayload() = 0;
                ebBuf.SetUsed(4);
                pThis->m_nCardStatus = MINISD_INSERTED;
                EmPostEvent(111, ebBuf);
                pThis->m_nCountSectors = nu_zenr_gbi_get_tfcard_blocks();
                kprintf("MiniSD: CardDeviceInsert, Capacity is %d sectors\n",
                                                        pThis->m_nCountSectors);
            }
            else if (stat == MINISD_REMOVED) {
                pThis->m_nCardStatus = MINISD_REMOVED;
                MemoryBuf_<16> ebBuf;
                ebBuf.SetUsed(4);
                *(int *)ebBuf.GetPayload() = 0;
                EmPostEvent(112, ebBuf);
            }
            else if (stat == MINISD_FINISHED) {
                break;
            }
        }

    }
    return NOERROR;
}

const Int32 g_SectorSize = 512;

ECode CMiniSd::Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    Uint32 ret = 0;
    UInt32 uBegSec = u64Offset / g_SectorSize;
    UInt32 uSectors = bytesToRead / g_SectorSize;

    if (bytesToRead == 0 ||
        (u64Offset & 511) ||
        (bytesToRead & 511)) {
        return E_INVALID_ARGUMENT;
    }

    Lock();

    ret = zapi_FS->TFCardIO(0, uBegSec, pBuffer->GetPayload(), uSectors, TRUE);

    Unlock();

    if (ret) {
        kprintf("MiniSD: read image file failed, ec = %d\n", ret);
        return E_FS_IO_ERROR;
    }
    pBuffer->SetUsed(uSectors * g_SectorSize);
    return NOERROR;
}

ECode CMiniSd::Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf &buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    Uint32 ret = 0;
    UInt32 uBegSec = u64Offset / g_SectorSize;
    UInt32 uWrite = buffer.GetUsed();

    if (uWrite == 0 ||
        (u64Offset & 511) ||
        (uWrite & 511)) {
        return E_INVALID_ARGUMENT;
    }

    Lock();

    ret = zapi_FS->TFCardIO(0, uBegSec, buffer.GetPayload(),
                            uWrite / g_SectorSize, FALSE);

    Unlock();

    if (ret) {
        kprintf("MiniSD: write(%d, %d) image file failed, ec = %d\n",
                                                    uBegSec, uWrite, ret);
        return E_FS_IO_ERROR;
    }

    *pBytesWritten = uWrite;

    return NOERROR;
}

ECode CMiniSd::Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf &inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    switch (nControlCode) {
        case MINISD_IOCTL_OPEN: {
            Uint32 ret = (Uint32)-1;
            ret = zapi_FS->TFCardOpen();
            if (ret) {
                kprintf("MiniSD: Control(MINISD_IOCTL_OPEN) failed, ec = %d\n", ret);
                return E_FS_IO_ERROR;
            }
            break;
        }
        case MINISD_IOCTL_CLOSE: {
            Uint32 ret = (Uint32)-1;
            ret = zapi_FS->TFCardClose();
            if (ret) {
                kprintf("MiniSD: Control(MINISD_IOCTL_CLOSE) failed, ec = %d\n", ret);
                return E_FS_IO_ERROR;
            }
            break;
        }
        case MINISD_IOCTL_CAPACITY: {
            Uint32 nCountSectors = 0;

            if (pOutBuffer == NULL || pOutBuffer->GetCapacity() < (Int32)sizeof(nCountSectors)) {
                return E_INVALID_ARGUMENT;
            }

            nCountSectors = nu_zenr_gbi_get_tfcard_blocks();
            pOutBuffer->Copy((PByte)&nCountSectors, sizeof(nCountSectors));
            pOutBuffer->SetUsed(sizeof(nCountSectors));
            break;
        }
        default:
            return E_NOT_IMPLEMENTED;
    }

    return NOERROR;
}

EXTERN IDeviceDriver * CDECL CreateTiMiniSD(uint_t uDeviceNo, void *pvParameter)
{
    CMiniSd* pDriver = new CMiniSd;

    return pDriver;
}

